import math_generic.Vector2;
import math_generic.Vector3;

public class Camera 
{
	final static float PI_OVER_180 = (float) Math.PI/180;
	final static float MAX_VELOCITY_WALK = 0.5f;
	final static float MAX_VELOCITY_RUN = 2.0f;
	
	Vector3 position; 
	Vector2 angle; 	 
	Vector3 velocity;  /* Direction vector (also velocity) */
	Vector3 lookingAt;
	
	boolean cameraFollowObj; /* Camera follows an object */
	boolean cameraLocked;
	//dynObj targetObject;	/* Camera follows this object */
	
	/* Re-usable variables */
	Vector2 vector_2d;
	Vector3 Vector_3d;
	float distanceWalked;
	boolean running;
	float wobbleCoeff;
	
	public Camera(float xpos, float ypos, float zpos)
	{
		/* Initialize re-usable variables */
		vector_2d = new Vector2(0, 0);
		Vector_3d = new Vector3(0, 0, 0);
		
		position = new Vector3(xpos, ypos, zpos);	  	  /* Position at the origin */
		angle = new Vector2(0, 0); 	  
		velocity = new Vector3(0, 0, 0);	  /* How fast it is moving */
		lookingAt = new Vector3(0, 0, -1.0f);
		
		/* Free camera */
		cameraLocked = false; 
		cameraFollowObj = false;
		
		distanceWalked = 0;
		running = false;
		wobbleCoeff = 3.0f;
		
	}
	
	public Vector3 getCameraPosition()
	{
		return position;
	}
	
	public Vector2 getCameraAngle()
	{
		return angle;
	}
	
	/* Camera can be locked on interactive objects (i.e. enemies, other players, treasure chests, etc) */
	public boolean isCameraLocked()
	{
		return cameraLocked;
	}

	/*public void lockCameraOn(dynObj)
	{
		//Camera is locked on its current position
		if(dynObj == null)
		{
			
		}
		else	// camera locked on an object (it will follow the object)
		{
			cameraLocked = true;
			targetObject = dynObj;
		}
	}*/
	
	public void freeCamera()
	{
		cameraLocked = false;
		//targetObject = null;
	}
	
	public void setCameraAngle(float newx, float newy)
	{
		angle.x = newx;
		angle.y = newy;
		
		if(angle.x < 0)
			angle.x = 359;
		else if(angle.x > 359)
			angle.x = 0;
		
		if(angle.y >= 90)
			angle.y = 89;
		else if(angle.y <= -90)
			angle.y = -89;
		
		/* Changing the angle, changes the viewing direction */
		setLookingAt(1.0f * (float) Math.sin(angle.x*PI_OVER_180),
				     1.0f * (float) Math.tan(angle.y*PI_OVER_180), 
				    -1.0f * (float) Math.cos(angle.x*PI_OVER_180));	
		
	}
	
	public void incrCameraPosition(float xincr, float yincr, float zincr)
	{
		position.x += xincr;
		position.y += yincr;
		position.z += zincr;
	}
	
	public void setCameraHeight(float h)
	{
		position.y = h;
	}
	
	public void setLookingAt(float newx, float newy, float newz)
	{
		lookingAt.x = newx;
		lookingAt.y = newy;
		lookingAt.z = newz;
	}
	
	public void incrLookingAt(float xincr, float yincr, float zincr)
	{
		lookingAt.x = xincr;
		lookingAt.y = yincr;
		lookingAt.z = zincr;
	}
	 
	public void incrCameraAngle(float xincr, float yincr)
	{
		setCameraAngle(angle.x + xincr,
					   angle.y + yincr);
		
		
		//System.out.println("hor_angle = " + angle.x);
		//System.out.println("vert_angle = " + angle.y);
	}
	
	public Vector3 lookingWhere()
	{
		return lookingAt;
	}
	
	public Vector3 getCameraVelocity()
	{
		return velocity;
	}
	
	/* Calculates the norm of the velocity vector */
	public float getDirectionVelocity()
	{
		return Vector3.magnitude(velocity.x, velocity.y, velocity.z);
	}
	
	public void printVelocity()
	{
		System.out.print("x = " + velocity.x);
		System.out.print(" y = " + velocity.y);
		System.out.print(" z = " + velocity.z);
		System.out.println(" norm = " + velocity.magnitude());
	}
	
	public void printPosition()
	{
		System.out.print("x = " + position.x);
		System.out.print("\ty = " + position.y);
		System.out.println("\tz = " + position.z);
	}
	
	public void incrCameraVelocity(float xincr, float yincr, float zincr)
	{
		velocity.x += xincr;
		velocity.y += yincr;
		velocity.z += zincr;
	}
	
	public void setCameraVelocity(float newx, float newy, float newz)
	{
		velocity.x = newx;
		velocity.y = newy;
		velocity.z = newz;
	}
	
	public void toggleRunning()
	{
		running = !running;
	}
	
	/* Relative to the heading (angle) */
	public void moveForward(float vel)
	{
		if(running)
			vel = Math.signum(vel)*MAX_VELOCITY_RUN;
		
		velocity = Vector3.calculateResultant(vel, angle.x*PI_OVER_180, 0, false);	
		move();
	}
	
	/* Relative to the heading (angle) */
	public void moveRight(float vel)
	{
		velocity = Vector3.calculateResultant(vel, angle.x*PI_OVER_180, 0, true);
		move();
	}
	
	/* Relative to the heading (angle) */
	public void moveUp(float vel)
	{
		velocity.y = vel;
		move();
	}
	
	public void checkVelLimits(Vector3 vel)
	{
		if(vel.x > MAX_VELOCITY_RUN)
			vel.x = MAX_VELOCITY_RUN;
		
		if(vel.y > MAX_VELOCITY_RUN)
			vel.y = MAX_VELOCITY_RUN;
		
		if(vel.z > MAX_VELOCITY_RUN)
			vel.z = MAX_VELOCITY_RUN;
	}
	
	/* Maybe automatic move (no need to press anything. CPU players??) */
	public void move()
	{
		/* Distance walked */
		distanceWalked += Vector2.distanceFromOrigin(velocity.x, velocity.z);
		System.out.println("distance walked = " + distanceWalked);
		
		position.x += velocity.x;
		position.y += velocity.y;
		position.z -= velocity.z;
		
		/* Walk wobble */
		if(running)
			wobbleCoeff = 6.0f;
		else
			wobbleCoeff = 3.0f;
		
		position.y += 0.033*wobbleCoeff*Math.sin(distanceWalked/wobbleCoeff);
	}
}
